NATインスタンスで通信元インスタンスによって接続元IPアドレスを切り替える
はじめに
AWSで、外部からのアクセスを遮断、あるいは外部へのアクセスを制限することを目的に、外部への通信経路を持たないプライベートサブネットを作成するパターンがあります。この場合プライベートサブネットに配置したインスタンスは外部へ一切通信が出来ないため、ミドルウェアのアップデートや外部サービスとの通信などは、外部アクセスが可能なパブリックサブネットに配置したNATインスタンスを経由するように設計します。
しかし、場合によっては通信元のインスタンスによって、外部に接続する際の接続元のグローバルIPアドレスを切り替えたい場合があります。例えば外部サービスにて接続元グローバルIPアドレスにより振り分けを行っていて、接続元グローバルIPアドレスが全て同じだと正常動作しない場合などです。
この「NATインスタンスで、通信元インスタンスによって、接続元のグローバルIPアドレスを切り替えたい」というwishが今回のスタート地点です。
やってみる
NATインスタンスはAmazon Linux AMI2014.03で構築しました。NATインスタンス用AMIは使っていません。
準備
NATインスタンスをLaunchする際に、Network Interfaceを2つ割り当てておきます。
また、NATインスタンスのSecurity Groupでは、通信元となるプライベートサブネットに配置されたインスタンスからの通信は許可しておきます。
プライベートサブネットのルーティングテーブルでは、NATインスタンスのネットワークインターフェースをデフォルトゲートに指定します。
NATインスタンスでは、Source/Dest. CheckをDisabledにする必要があります...が、EC2 DashboardのInstances画面のActionから実行出来る[Change Source/Dest. Check]では、eth0しか変更されません。
このため、EC2 DashboardのNetwork Interfaces画面のActionから、2つのネットワークインターフェースに対しそれぞれSource/Dest. CheckをDisabledに変更します。
NATインスタンスの設定
さて、本題です。今回のwishを実現するためにNATインスタンスで行うことは
- iptablesによるIPマスカレードの設定
- ip ruleによるルーティングテーブルの設定
の2つになります。
iptablesによるIPマスカレードの設定
まず/etc/sysctl.confを変更し、IPマスカレードをする為のカーネルパラメータの変更を行います。
### net.ipv4.ip_forwardが無効になっていることを確認。併せてsend_redirectsも変更するので確認。 $ sysctl net.ipv4.ip_forward net.ipv4.conf.eth0.send_redirects net.ipv4.conf.eth1.send_redirects net.ipv4.ip_forward = 0 net.ipv4.conf.eth0.send_redirects = 1 net.ipv4.conf.eth1.send_redirects = 1 ### /etc/sysctl.confに設定値を記述。 $ sudo vi /etc/sysctl.conf net.ipv4.ip_forward = 1 net.ipv4.conf.eth0.send_redirects = 0 net.ipv4.conf.eth1.send_redirects = 0 ### /etc/sysctl.confの内容を即時反映。 $ sudo sysctl -p /etc/sysctl.conf ### 反映された値を確認する。 $ sysctl net.ipv4.ip_forward net.ipv4.conf.eth0.send_redirects net.ipv4.conf.eth1.send_redirects net.ipv4.ip_forward = 1 net.ipv4.conf.eth0.send_redirects = 0 net.ipv4.conf.eth1.send_redirects = 0
次にiptablesコマンドにて、eth0とeth1の2つのネットワークインターフェースで、それぞれIPマスカレードを有効にします。通信元IPアドレスではそのVPC全体のIPサブネットを指定しています。
$ sudo iptables -t nat -A POSTROUTING -o eth0 -s 172.31.0.0/16 -j MASQUERADE $ sudo iptables -t nat -A POSTROUTING -o eth1 -s 172.31.0.0/16 -j MASQUERADE ### 設定された値を確認 $ sudo iptables -nvL -t nat Chain PREROUTING (policy ACCEPT 1 packets, 60 bytes) pkts bytes target prot opt in out source destination Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * eth0 172.31.0.0/16 0.0.0.0/0 0 0 MASQUERADE all -- * eth1 172.31.0.0/16 0.0.0.0/0 ### 設定した値を保存 $ sudo service iptables save
ip ruleによるルーティングテーブルの設定
iptablesでIPマスカレードの設定を行っただけでは、全ての通信はeth0を経由してしまいます。EC2でネットワークインターフェースを2つ追加すると、以下のように「eth1に割り当てられたIPアドレスのみがIPルールテーブルID:10001を使う」というように設定されています。
$ sudo ip rule 0: from all lookup local 32765: from 172.31.8.208 lookup 10001 32766: from all lookup main 32767: from all lookup default
このIPルールテーブルID:10001はeth1で使われています。
$ sudo cat /etc/sysconfig/network-scripts/route-eth1 default via 172.31.0.1 dev eth1 table 10001 default via 172.31.0.1 dev eth1 metric 10001
そこで、eth1から出力したい(eth1に割り当てられたグローバルIPアドレスを接続元IPアドレスとして使いたい)インスタンスからの通信は、全てのこのIPルールテーブルID:10001を使うように設定します。 なお、IPアドレスを直接指定してしまうと、インスタンスのプライベートIPアドレスが変更された場合に動作しなくなってしまうため、AWS CLIなどを組み合わせて適切なIPアドレスが定義されるような仕組みを用いるべきです。
### EC2-Bはeth1から出力されるようにip ruleを追加 $ sudo ip rule add from 172.31.26.176 lookup 10001 ### 設定値の確認 $ sudo ip rule 0: from all lookup local 32764: from 172.31.26.176 lookup 10001 32765: from 172.31.8.208 lookup 10001 32766: from all lookup main 32767: from all lookup default
これで設定は完了です。
動作確認
EC2-Aから外部に接続し、接続元IPアドレスを確認してみます。
$ curl http://www.ugtop.com/index.shtml ---snip--- 54.178.213.178<BR> ec2-54-178-213-178.ap-northeast-1.compute.amazonaws.com<BR> ---snip---
同じようにEC2-Bから外部に接続し、接続元IPアドレスを確認してみます。
$ curl http://www.ugtop.com/index.shtml ---snip--- 54.178.197.195<BR> ec2-54-178-197-195.ap-northeast-1.compute.amazonaws.com<BR> ---snip---
それぞれ違うIPアドレスになっていますね!
まとめ
今回は少々トリッキーな使い方について調べてみました。NATインスタンスを複数Launchするという手もありますが、今回ご紹介した手段を用いることでインスタンス数を増やさずに済みます。